#ifndef _FILESYSTEM_H_
#define _FILESYSTEM_H_

#include <set>
#include <vector>
#include <string>
#include <ext/hash_map>
#include <errno.h>
#include <fuse.h>
#include <fuse.h>
#include <fcntl.h>
#include <iostream>

#include "support.h"
#include "Database.h"
#include "Tag.h"

const std :: string DATABASE_FILE = "tagfsdb";
const std :: string FILES_DIR = "Files";

/* files/directory constants */
const int RIGHTS = 0755;
const int ROOT_DIR_NLINK = 2;
const int DEFAULT_DIR_NLINK = 1;
const int FILE_NLINK = 1;

class FileSystem {
		
	private:
		Database *database;
		__gnu_cxx :: hash_map<std :: string, Tag> *tags;
		std :: string repo_path;
		std :: string db_path;
		std :: string files_path;
		
		/*
		 * returns children of a directory that is constructed from tags only
		 * 
		 * @1: tags that should not be returned
		 * 
		 * returns: vector with all tags nad Files directory on success, 0 otherwise
		 */
		std :: vector<std :: string>* get_tag_children(const std :: set <Tag *> &);
		
		/*
		 * returns children of Files directory based on given tags
		 * 
		 * @1: tags that determine contents of Files directory
		 * 
		 * returns: vector with contents of Files directory on success, 0 otherwise
		 */
		std :: vector<std :: string>* get_direct_children(const std :: set <Tag *> &);
		
		/*
		 * returns children of directory representing ambiguous file in Files directory
		 * 
		 * @1: name of the file
		 * @2: tags that determine contents of Files directory
		 * 
		 * returns: vector with at least two files from the ambiguous file's directory
		 * on success, 0 otherwise
		 */
		std :: vector<std :: string>* get_deep_children(const std :: string &, const std :: set <Tag *> &);
		
		/*
		 * adds/deletes tag to the filesystem
		 * 
		 * @1: name of the tag
		 * 
		 * returns: 0 on success, -1 otherwise
		 */
		int add_tag(const std :: string &);		
		int del_tag(const std :: string &);

		/*
		 * gets statistics of a file with given tags
		 * 
		 * @1: name of the file
		 * @2: set of tags of the file
		 * @3: pointer to a statistics structure
		 * 
		 * returns: 0 on a success, -1 otherwise
		 */
		int get_direct_stat(const std :: string &, const std :: set<Tag *> &, struct stat *);
		int get_deep_stat(const std :: string &, const std :: set<Tag *> &, struct stat *);
		
		/*
		 * gets physical path of a file inside the repo
		 * 
		 * @1: virtual path to the file
		 * @2: reference to a string, where result will be stored
		 * 
		 * returns: 0 on success; -1 otherwise
		 */
		int get_real_path(const std :: string &, std :: string &);
		
		/*
		 * retrieves all tags held in a given path
		 * 
		 * @1: path to an object in the filesystem
		 * @2: reference to a vector, where all tags will be stored
		 * 
		 * returns: 0 on a sucees; -1 otherwise
		 */
		int get_tags(const std :: string &, std :: vector<Tag> &);
		
		/*
		 * retrieves filename held in a given path
		 * 
		 * @1: path to an object in the filesystem
		 * @2: reference to a string, where filename will be stored
		 * 
		 * returns: 0 if filename was retrieved successfully; -1 if path
		 * was not proper path to a file in the virtual system
		 */
		int get_file_name(const std :: string &, std :: string &name);
		
	public:
		/* 
		 * standard constructor
		 *
		 * @1: path, where information about the filesystem are stored
		 * 
		 * returns: None
		 */
		FileSystem(const std :: string &);
		
		/*
		 * returns children of a given path
		 * 
		 * @1: directory's path
		 * 
		 * returns: vector of names of children
		 */
		std :: vector<std :: string>* get_children(const std :: string &);
		
		/*
		 * sets file statistics for a given file
		 * 
		 * @1: path to the file
		 * @2: statistics to be stat
		 * 
		 * returns: 0 on success; -1 otherwise
		 */
		int get_stat(const char *, struct stat *);
		int get_stat(const std :: string &, struct stat *);
		
		/*
		 * adds a new directory to the filesystem
		 * 
		 * @1: directory's path; only last step is used as a new dir name
		 * 
		 * returns: 0 on success, -1 otherwise
		 */
		int add_dir(const std :: string &);
		
		/*
		 * adds a new file to the filesystem with given tags
		 * 
		 * @1: file's path; last step is used as file's name, while the
		 * rest is treated as it's tags
		 * 
		 * returns: 0 on success, -1 otherwis
		 */ 
		int add_file(const std :: string &);

		/*
		 * deletes single directory from the filesystem
		 * 
		 * @1: directory's path; only last step is taken as a deleted
		 * file's name
		 * 
		 * returns: 0 on success, -1 otherwise
		 */
		int del_dir(const std :: string &);
		
		int write_file(const std :: string &, const char *, size_t, off_t);
		int read_file(const std :: string &, char *, size_t, off_t);
		int utime_file(const std :: string &, const struct timespec tv[2]);
		
		/*
		 * if file has any tags, it deletes tags given in the path; if 
		 * file doesn't have any tags it is permanently deleted from the
		 * system
		 * 
		 * @1: path to file, turned into a set of tags
		 * 
		 * returns: 0 on success, -1 otherwise
		 */ 
		int delete_file(const char *);
		int delete_file(const std :: string &);
		
		~FileSystem();
		
};

#endif
